from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import mail
from google.appengine.ext import db
from django.utils import simplejson
from dateutil import parser
from models import NextAction, ActionCategory, Project, Task

import utils
import datetime
import logging
import traceback
import os

class RPCHandler(webapp.RequestHandler):
  """ Allows the functions defined in the RPCMethods class to be RPCed."""
  def __init__(self):
    webapp.RequestHandler.__init__(self)
    self.methods = RPCMethods()
 
  def get(self):
    func = None
   
    action = self.request.get('action')
    if action:
      if action[0] == '_':
        self.error(403) # access denied
        return
      else:
        func = getattr(self.methods, action, None)
   
    if not func:
      self.error(404) # file not found
      return
     
    args = ()
    while True:
      key = 'arg%d' % len(args)
      val = self.request.get(key)
      if val:
        args += (simplejson.loads(val),)
      else:
        break
    result = func(*args)
    self.response.out.write(simplejson.dumps(result))

  def post(self):
    args = simplejson.loads(self.request.body)
    func, args = args[0], args[1:]
   
    if func[0] == '_':
      self.error(403) # access denied
      return
    
    meth = func
    func = getattr(self.methods, func, None)
    if not func:
      logging.error("'%s' is not a valid RPC method" % meth)
      self.error(404) # file not found
      return

    result = func(*args)
    self.response.out.write(simplejson.dumps(result))



class RPCMethods:
  """ Defines the methods that can be RPCed.
  NOTE: Do not allow remote callers access to private/protected "_*" methods.
  """
  
  def error(self, msg):
      return '!!ERROR!! %s' % msg
      
  def DeleteProject(self, *args):
    project = Project.get(args[0])
    project.delete_everything()
    return args[1]
      
  def DeleteProjectTasks(self, *args):
    try:
      project = Project.get(args[0])
      project.delete_tasks()
      return 'Successfully deleted all of the tasks'
    except BaseException, e:
      return self.error('Unable to delete all of the tasks: %s' % e)
      
  def AddEditProjectTask(self, *args):
    logging.info("Received request to add a project task")
    try:
      task_key = args[5]
      task = Task.get(task_key) if task_key else None
      if not task:
        task = Task()
      task.user = utils.cuser()
      task.action = args[1]
      task.has_due_date = args[2]
      task.due_date = parser.parse(args[3]) if task.has_due_date else None
      task.is_complete = args[4]
      task.put()
      try:
        project = Project.get(args[0])
        project.tasks.append(task.key())
        project.put()
        task.parent_project = project
        task.put()
      except BaseException, e:
        task.delete()
        return self.error("Unable to add project task '%s': %s" % (task.action, e))
      else:
        return task.as_json()
    except BaseException, e:
      logging.error(traceback.format_exc())
      return self.error('Unable to add project task: %s' % e)
  
  def EditProjectDescription(self, *args):
    project = Project.get(args[0])
    new_desc = args[1]
    project.description = new_desc
    project.put()
    return new_desc
      
  def MarkProjectComplete(self, *args):
    project = Project.get(args[0])
    project.complete()
    markTasksComplete = args[1]
    logging.info("Marking '%s' complete - %s marking sub-tasks complete" % (project, 'and' if markTasksComplete else 'not'))
    if markTasksComplete:
      for task in project.subtasks():
        task.complete()
    return args[2]
      
  def MarkProjectNotComplete(self, *args):
    Project.get(args[0]).uncomplete()
    return args[1]
      
  def AddProject(self, *args):
    project = Project()
    project.user = utils.cuser()
    project.description = args[0]
    project.project_type = args[1]
    project.is_complete = False
    project.put()
    return project.as_json()
      
  def EmailNextActions(self, *args):
    try:
      user_address = utils.cuser().email()

      if not mail.is_email_valid(user_address):
        return self.error('Unable to send email report becasue %s is not a valid address' % user_address)
      else:
        sender_address = user_address
        subject = "Next Actions email report"
        body = "HTML body contains Next Actions"
        template_values = {'actions': utils.actions()}    
        path = os.path.join(os.path.dirname(__file__), 'action_email.html')
        mail.send_mail(sender_address, user_address, subject, body, html=template.render(path, template_values))
        return 'Successfully emailed Next Actions report to %s' % user_address
    except BaseException, e:
      logging.error(traceback.format_exc())
      return self.error('Unable to send email report: %s' % e)
      
  def MarkTaskNotComplete(self, *args):
    task = Task.get(args[0])
    task.is_complete = False
    task.complete_date = None
    task.put()
    return args[1]
      
  def MarkTaskComplete(self, *args):
    try:
      task = Task.get(args[0])
      task.complete()
      return args[1]    
    except BaseException, e:
      logging.error(traceback.format_exc())
      return self.error("Unable to mark '%s' as complete: %s" % (args[0], e))
  
  def GetAction(self, *args):
    key = args[0]
    try:
      return NextAction.get(key).as_json()
    except BaseException, data:
      return self.error('Unable to retrieve "%s": %s' % (key, data))
  
  def GetActions(self, *args):
    return [act.as_json() for act in utils.actions()]
  
  def AddEditAction(self, *args):
    try:
      key = args[8]
      action = NextAction.get(key) if (key != '') else None
      if not action:
        action = NextAction()
      action.user = utils.cuser()
      action.action = args[0]
      action.category = ActionCategory.gql("where name=:name", name=args[1]).get()
      phone = args[2]
      action.phone_number = phone if (phone != '') else None
      url = args[3]
      action.url = url if (url != '') else None
      action.tag = args[4]
      action.has_due_date = args[5]
      action.due_date = parser.parse(args[6]) if action.has_due_date else None
      action.is_complete = False
      action.put()
      response = action.as_json()
      response.update({'edit_row_id': args[7]})
      return response
    except BaseException, data:
      logging.error(traceback.format_exc())
      return self.error('Unable to add next action: %s' % data)

  def DeleteTask(self, *args):
    # The JSON encoding may have encoded integers as strings.
    # Be sure to convert args to any mandatory type(s).
    key = args[0]
    try:
      task = Task.get(key)
      proj = task.parent_project
      if proj:
        proj.tasks.remove(task.key())
        proj.put()
      task.delete()
    except BaseException, data:
      logging.error(traceback.format_exc())
      return self.error('Unable to delete "%s": %s' % (key, data))
    return args[1]
